#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
/*
*   一次完整的数据为 40bit，按照高位在前，低位在后的顺序传输
*   数据格式为：8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和
*   DHT11时序：
*   1、主机拉低最少18ms后拉高，表示开始
*   2、从机收到开始信号后，先拉低80us，再拉高80us，然后开始发送数据
*   3、0 ：50us低电平 + 26～28us高电平
*      1 ：50us低电平 + 70us高电平
*/
typedef struct DHT11Dev
{
    int iDht11Gpio;
    struct device_node *ptDeviceNode; /* 设备节点 */
    struct miscdevice tDHT11_MiscDev;
} T_DHT11Dev, *PT_DHT11Dev;

T_DHT11Dev g_tDHT11Dev;

static int dht11_init(void)
{
    int iTime = 0;
    int iGpio = g_tDHT11Dev.iDht11Gpio;

    /*主机发送开始信号*/
    gpio_direction_output(iGpio,1);
    udelay(10);
    gpio_set_value(iGpio,0);
    mdelay(20);
    gpio_set_value(iGpio,1);
    udelay(20);
    gpio_direction_input(iGpio);

    /*等待从机应答*/
    while(gpio_get_value(iGpio) != 0)
    {
        udelay(1);
        iTime++;
        if(iTime > 100)
        {
            return -1;
        }
    }
    /*从机应答1*/
    iTime = 0;
    while(gpio_get_value(iGpio) == 0)
    {
        udelay(1);
        iTime++;
        if(iTime > 100)
        {
            return -2;
        }
    }
    /*从机应答2*/
    iTime = 0;
    while(gpio_get_value(iGpio) == 1)
    {
        udelay(1);
        iTime++;
        if(iTime > 100)
        {
            return -3;
        }
    }
    return 0;
}
static int dht11_read_byte(unsigned char *ucData)
{
    int i = 0,iTime = 0;
    int iGpio = g_tDHT11Dev.iDht11Gpio;
    unsigned char data = 0x00;

    for(i = 0; i < 8 ;i++ )
    {
        iTime = 0;
        while(gpio_get_value(iGpio) == 0)
        {
            udelay(1);
            iTime++;
            if(iTime > 100)
            {
                return -EINVAL;
            }
        }
        /*延时一段时间*/
        udelay(50);
        if (gpio_get_value(iGpio) == 1)
        {
            data |= 1 << (7 - i) ; /*高位在前*/
            while (gpio_get_value(iGpio) == 1)
            {
                udelay(1);
                iTime++;
                if (iTime > 100)
                {
                    return -EINVAL;
                }
            }
        }
    }
    memcpy(ucData,&data,1);
    return 0;
    
}

static int DHT11_OPEN(struct inode *ptinode, struct file * filp)
{
    filp->private_data = &g_tDHT11Dev;
    return 0;
}
static ssize_t DHT11_READ(struct file *filp, char __user *buf, size_t n, loff_t *offt)
{
    int iError,i;
    unsigned char ucData[5] = {0x00};
    PT_DHT11Dev ptDev = filp->private_data;
    iError = dht11_init();
    if(iError < 0)
    {
        printk("dht11_init  %d error!",iError);
        return iError;
    }

    gpio_direction_input(ptDev->iDht11Gpio);

    for (i = 0; i < 5; i++)
    {
        iError = dht11_read_byte(&ucData[i]);
        if (iError < 0)
        {
            printk("dht11 read byte %d error!",iError);
            return iError;
        }
    }
    iError = copy_to_user(buf, &ucData, sizeof(ucData)); 

    return iError;
}
static ssize_t DHT11_WRITE(struct file *filp, const char __user *buf, size_t n, loff_t *offt)
{
    return 0;
}
static struct file_operations tDHT11_Ops =
    {
        .owner = THIS_MODULE,
        .open = DHT11_OPEN,
        .read = DHT11_READ,
        .write = DHT11_WRITE,
};
static int dht11_probe(struct platform_device *ptDev)
{
    int iError;
    g_tDHT11Dev.ptDeviceNode = of_find_node_by_name(NULL,"dht11");
    if(g_tDHT11Dev.ptDeviceNode == NULL)
    {
        printk("dht11 node not find!\r\n");
        return -EINVAL;
    }
    g_tDHT11Dev.iDht11Gpio = of_get_named_gpio(g_tDHT11Dev.ptDeviceNode,"dht11-gpio",0);
    if(!gpio_is_valid(g_tDHT11Dev.iDht11Gpio))
    {
        printk("can't get dht11-gpio!\r\n");
        return -EINVAL;
    }

    iError = devm_gpio_request(&ptDev->dev,g_tDHT11Dev.iDht11Gpio,"dht11");
    if (iError)
    {
        printk("devm_gpio_request failed!\r\n");
        return -EFAULT;
    }

    gpio_direction_output(g_tDHT11Dev.iDht11Gpio,1);

    g_tDHT11Dev.tDHT11_MiscDev.name  = "dht11";
    g_tDHT11Dev.tDHT11_MiscDev.minor = MISC_DYNAMIC_MINOR;
    g_tDHT11Dev.tDHT11_MiscDev.fops  = &tDHT11_Ops;

    iError = misc_register(&g_tDHT11Dev.tDHT11_MiscDev);
    if (iError < 0)
    {
        printk("misc device register failed!\r\n");
        return -EFAULT;
    }
    dev_info(&ptDev->dev, "dht11 device and driver matched successfully!\n");
    return 0;
}
static int dht11_remove(struct platform_device *ptDev)
{
    gpio_set_value(g_tDHT11Dev.iDht11Gpio,0);
    misc_deregister(&g_tDHT11Dev.tDHT11_MiscDev);
    dev_info(&ptDev->dev, "DHT11 driver has been removed!\n");
    return 0;
}
static const struct of_device_id dht11_of_match[] = {
    {.compatible = "dht11"},
    {},
};
static struct platform_driver tDHT11_Driver = {
    .driver = {
        .name = "dht11",
        .of_match_table = dht11_of_match,
    },
    .probe = dht11_probe,
    .remove = dht11_remove,
};
module_platform_driver(tDHT11_Driver);

// //驱动入口函数
// static int __init irqkey_init(void)
// {
// 	return platform_driver_register(&g_tDHT11Dev);
// }
// static void __exit irqkey_exit(void)
// {
//     platform_driver_unregister(&g_tDHT11Dev);
// }

// module_exit(irqkey_exit);
// module_init(irqkey_init);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Destiny");